// synch.cc 
//    Routines for synchronizing threads.  Three kinds of
//    synchronization routines are defined here: semaphores, locks 
//       and condition variables (the implementation of the last two
//    are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"

//----------------------------------------------------------------------
// Semaphore::Semaphore
//     Initialize a semaphore, so that it can be used for synchronization.
//
//    "debugName" is an arbitrary name, useful for debugging.
//    "initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
//     De-allocate semaphore, when no longer needed.  Assume no one
//    is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
//     Wait until semaphore value > 0, then decrement.  Checking the
//    value and decrementing must be done atomically, so we
//    need to disable interrupts before checking the value.
//
//    Note that Thread::Sleep assumes that interrupts are disabled
//    when it is called.
//----------------------------------------------------------------------

void
Semaphore::P()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);    // disable interrupts
    
    while (value == 0) {             // semaphore not available
		queue->Append((void *)currentThread);    // so go to sleep
		currentThread->Sleep();
    } 
    value--;                     // semaphore available, 
								 // consume its value
    
    interrupt->SetLevel(oldLevel);    // re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
//     Increment semaphore value, waking up a waiter if necessary.
//    As with P(), this operation must be atomic, so we need to disable
//    interrupts.  Scheduler::ReadyToRun() assumes that threads
//    are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);
	
    thread = (Thread *)queue->Remove();
    if (thread != NULL)       // make thread ready, consuming the V immediately
		scheduler->ReadyToRun(thread);
    value++;
    interrupt->SetLevel(oldLevel);
}

// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!
//----------------------------------------------------------------------
// Lock::Lock
//     Initialize a lock, so that it can be used for monitor.
//
//    "debugName" is an arbitrary name, useful for debugging.
//----------------------------------------------------------------------
Lock::Lock(char* debugName) 
{
    name = debugName;
    state = 0;        //means free
    ownerThread = NULL;   //means no owner
    lockWaitQueue = new List;
}

//----------------------------------------------------------------------
// Lock::~Lock
//     De-allocate lock, when no longer needed.  Assume no one
//    is still waiting for that lock!
//----------------------------------------------------------------------
Lock::~Lock() 
{
    delete lockWaitQueue;
}

//----------------------------------------------------------------------
// Lock::Acquire
//     acquire for a lock when it's the turn for that thread
//    this operation must be atomic, so we need to disable
//    interrupts. Other threads are disabled to call it.    
//----------------------------------------------------------------------
void 
Lock::Acquire()                            
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);        // disable interrupts
	
	
    Thread* threadAskLock = currentThread; 
	
    if(currentThread == ownerThread) {    //the owner already have this lock, so cannot acquire it again.
        interrupt->SetLevel(oldLevel);                // re-enable interrupts
        return;
    }
    
    if(!state){                            //is lock available
        setState(true);                                        //bool 1 means busy, that is make the lock busy
		ownerThread = currentThread;
    } else{
        //Add to queue
        lockWaitQueue->Append((void *)threadAskLock);
        threadAskLock->Sleep();
    }
	
	
    interrupt->SetLevel(oldLevel);                    // re-enable interrupts
}

//----------------------------------------------------------------------
// Lock::Release
//     Release a lock when owner of lock no longer need it.
//    This operation must be atomic, so we need to disable
//    interrupts. Other threads are disabled to call it.    
//----------------------------------------------------------------------
void 
Lock::Release()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);        // disable interrupts
    
    Thread* threadReleaseLock = currentThread; 
    if(threadReleaseLock != ownerThread){
		printf("ERROR: Lock::Release -- owner don't match %s %s",
			   currentThread->getName(), ownerThread->getName());
        interrupt->SetLevel(oldLevel);
        return;
    }
	
    if(!lockWaitQueue->IsEmpty())
	{
        Thread* nextThreadPtr = (Thread*) (lockWaitQueue->Remove());
        if (nextThreadPtr != NULL)
		{   
			// make thread ready
            scheduler->ReadyToRun(nextThreadPtr);
			ownerThread = nextThreadPtr;
            setState(true);
        }
    } 
	else
	{						//nobody waits for lock
        setState(false);	//free the lock
		ownerThread = NULL;
    }
	
	
    interrupt->SetLevel(oldLevel);                // re-enable interrupts
}

Condition::Condition(char* debugName) 
{ 
    name = debugName;
    condQueue = new List;
    waitingLock = NULL;
	
}
Condition::~Condition() 
{
    delete condQueue;
}
void Condition::Wait(Lock* conditionLock) 
{ 
    
    IntStatus oldLevel = interrupt->SetLevel(IntOff);
    
    if (conditionLock == NULL)
	{
		printf("ERROR: Condition::Wait -- null error wait  Calling  Thread Name =%s\n", currentThread->getName());
        interrupt->SetLevel(oldLevel);
        return;
    }
    
    if (waitingLock == NULL)
	{
        waitingLock = conditionLock;
    }
    
    if (waitingLock != conditionLock){
        printf("ERROR: Condition::Wait --  error != wait Wait %s != Condition lock %s \n", waitingLock->getName(), conditionLock->getName());
        interrupt->SetLevel(oldLevel);
        return;
    }
    
    condQueue->Append((void *)currentThread);
    conditionLock->Release();
    currentThread->Sleep();
    conditionLock->Acquire();
    interrupt->SetLevel(oldLevel);
}

void Condition::Signal(Lock* conditionLock) { 
    
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);
    
	if( waitingLock == NULL)
	{
		interrupt->SetLevel(oldLevel);
		return;
	}
    if (waitingLock != conditionLock){
        printf("ERROR: Condition::Signal -- error signal:%s %s\n",
			   conditionLock->getName(), waitingLock->getName());
        interrupt->SetLevel(oldLevel);
        return;
    }
    
    thread = (Thread *)condQueue->Remove();
	
    if (thread != NULL)
		scheduler->ReadyToRun(thread);
    
    if(condQueue->IsEmpty())
	{
        waitingLock = NULL;
    }
    interrupt->SetLevel(oldLevel);
}

void Condition::Broadcast(Lock* conditionLock)
{ 
    while (!condQueue->IsEmpty())
    {
        Signal(conditionLock);
    }
}

